package vivace.view;

import java.awt.FlowLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Hashtable;
import java.util.Observable;
import java.util.Observer;

import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import vivace.controller.AppController;
import vivace.model.Action;
import vivace.model.App;
import vivace.model.UI.Perspective;

/**
 * The toolbar for the application. 
 * The toolbar observes updates from both the App-class and the GUISettings-class
 */
public class AppToolBar extends JPanel implements Observer, ChangeListener, ItemListener {

	private static final long serialVersionUID = -2681788908338971919L;

	// Constants used by the zoom slider
	private final int MAXZOOM = 600;
	private final int MINZOOM = 50;
	private final int INITZOOM = 100;
	private final int TICK_SPACE = 10;

	private AppController controller;
	private ImageIcon 
		playIcon, pauseIcon, stopIcon, recordIcon,
		newIcon, openIcon, saveIcon, 
		saveAsIcon, undoIcon, redoIcon,
		createTrackIcon, removeTrackIcon,
		overviewIcon, pianoRollIcon, notationIcon,
		cursorIcon, pencilIcon, eraserIcon,
		keyboardIcon;
	private JLabel zoomLabel;
	private ButtonGroup perspectiveButtons;
	private VToggleButton overviewButton, pianoRollButton, notationButton, cursorButton, pencilButton, eraserButton, recordButton;
	private VButton playPauseButton, stopButton, newButton, openButton, saveButton, 
		saveAsButton, undoButton, redoButton, createTrackButton, removeTrackButton, keyboardButton;
	private VComboBox timeSignatureBox;
	private VSlider zoom;

	/**
	 * Constructor
	 */
	public AppToolBar() {

		// Tell the model to notify about all changes 
		App.addAppObserver(this);

		// Create a reference to the controller
		controller = new AppController();
		
		// Set the layout and initialize the GUI
		FlowLayout flowLayout = new FlowLayout(FlowLayout.LEFT, 0, 0);
		setLayout(flowLayout);
		initializeGUI();
		
	}
	
	private void initializeGUI(){
		
		FlowLayout toolBarLayout = new FlowLayout(FlowLayout.LEFT, 0, 0);
		
		/* Project toolbar */
		JToolBar projectToolBar = new JToolBar();
		projectToolBar.setLayout(toolBarLayout);
		projectToolBar.setFloatable(false);
		
		// Initialize toolbar icons
		newIcon = new ImageIcon("resources/project-new.png");
		openIcon = new ImageIcon("resources/project-open.png");
		saveIcon = new ImageIcon("resources/project-save.png");
		saveAsIcon = new ImageIcon("resources/project-save-as.png");
		undoIcon = new ImageIcon("resources/project-undo.png");
		redoIcon = new ImageIcon("resources/project-redo.png");
		createTrackIcon = new ImageIcon("resources/project-create-track.png");
		removeTrackIcon = new ImageIcon("resources/project-remove-track.png");
				
		// create 'new' button
		newButton = new VButton(newIcon,"tt_New");
		newButton.setActionCommand("new");
		newButton.addActionListener(controller);
		projectToolBar.add(newButton);
		
		//create 'open' button
		openButton = new VButton(openIcon,"tt_Open");
		openButton.setActionCommand("open");
		openButton.addActionListener(controller);
		projectToolBar.add(openButton);
		
		// create 'save' button
		saveButton = new VButton(saveIcon,"tt_Save");
		saveButton.setActionCommand("save");
		saveButton.addActionListener(controller);
		projectToolBar.add(saveButton);
		
		// create 'saveAs' button
		saveAsButton = new VButton(saveAsIcon,"tt_SaveAs");
		saveAsButton.setActionCommand("saveAs");
		saveAsButton.addActionListener(controller);
		projectToolBar.add(saveAsButton);
		
		// create 'undo' button
		undoButton = new VButton(undoIcon,"tt_Undo");
		undoButton.setActionCommand("undo");
		undoButton.addActionListener(controller);
		projectToolBar.add(undoButton);
		
		// create 'redo' button
		redoButton = new VButton(redoIcon,"tt_Redo");
		redoButton.setActionCommand("redo");
		redoButton.addActionListener(controller);
		projectToolBar.add(redoButton);
		
		// create 'createTrack' button
		createTrackButton = new VButton(createTrackIcon,"tt_CreateTrack");
		createTrackButton.setActionCommand("createTrack");
		createTrackButton.addActionListener(controller);
		projectToolBar.add(createTrackButton);
		
		// create 'removeTrack' button
		removeTrackButton = new VButton(removeTrackIcon,"tt_RemoveTrack");
		removeTrackButton.setActionCommand("removeTrack");
		removeTrackButton.addActionListener(controller);
		projectToolBar.add(removeTrackButton);
		
		projectToolBar.addSeparator();
		
		// add toolbar to panel
		this.add(projectToolBar);
	   
		/* Playback toolbar */
		JToolBar playbackToolBar = new JToolBar();
		playbackToolBar.setLayout(toolBarLayout);
		playbackToolBar.setFloatable(false);
		
		// Initialize toolbar icons
		playIcon = new ImageIcon("resources/playback-start.png");
		pauseIcon = new ImageIcon("resources/playback-pause.png");
		stopIcon = new ImageIcon("resources/playback-stop.png");
		recordIcon = new ImageIcon("resources/playback-record.png");
		
		// Create the 'play/pause' button
	    playPauseButton = new VButton(playIcon,"tt_Play");
	    playPauseButton.setActionCommand("play");
	    playPauseButton.addActionListener(controller);
	    playbackToolBar.add(playPauseButton);
	    
	    // Create the 'record' button
	    recordButton = new VToggleButton(recordIcon, "tt_Record");
	    recordButton.setActionCommand("record");
	    recordButton.addActionListener(controller);
	    playbackToolBar.add(recordButton);
	   
	    // Create the 'stop' button
	    stopButton = new VButton(stopIcon,"tt_Stop");
	    stopButton.setActionCommand("stop");
	    stopButton.addActionListener(controller);
	    playbackToolBar.add(stopButton);
		    
		// Create the panic button
		/* JButton panicButton = new JButton(panicIcon);
		panicButton.setActionCommand("panic");
		panicButton.addActionListener(controller);
	    playbackToolBar.add(panicButton); */
		
		playbackToolBar.addSeparator();
	    
	    this.add(playbackToolBar);
	    
	    /* Perspective toolbar */
	    JToolBar perspectiveToolBar = new JToolBar();
	    perspectiveToolBar.setLayout(toolBarLayout);
	    perspectiveToolBar.setFloatable(false);
	    
	    // create button group
	    perspectiveButtons = new ButtonGroup();
	    
	    // Initialize toolbar icons
	    overviewIcon = new ImageIcon("resources/perspective-overview.png");
	    pianoRollIcon = new ImageIcon("resources/perspective-piano-roll.png");
		notationIcon = new ImageIcon("resources/perspective-note-system.png");
		
		// create 'overview' button
		overviewButton = new VToggleButton(overviewIcon,"tt_Overview");
		overviewButton.setActionCommand("perspectiveOverview");
		overviewButton.addActionListener(controller);
		overviewButton.setSelected(true); // overview selected per default
		perspectiveButtons.add(overviewButton);
		perspectiveToolBar.add(overviewButton);
		
		// create 'piano roll' button
		pianoRollButton = new VToggleButton(pianoRollIcon,"tt_PianoRoll");
		pianoRollButton.setActionCommand("perspectivePianoRoll");
		pianoRollButton.addActionListener(controller);
		perspectiveButtons.add(pianoRollButton);
		perspectiveToolBar.add(pianoRollButton);
		
	    // create 'notation' button
		notationButton = new VToggleButton(notationIcon, "tt_Notation");
		notationButton.setActionCommand("perspectiveNotation");
		notationButton.addActionListener(controller);
		perspectiveButtons.add(notationButton);
		perspectiveToolBar.add(notationButton);
			
		perspectiveToolBar.addSeparator();
		
		// add toolbar to panel
		this.add(perspectiveToolBar);
		
		/* Tool toolbar */
	    JToolBar toolToolBar = new JToolBar();
	    toolToolBar.setLayout(toolBarLayout);
	    toolToolBar.setFloatable(false);
	    
	    // create button group
	    ButtonGroup toolButtons = new ButtonGroup();
	    
	    // Initialize toolbar icons
		cursorIcon = new ImageIcon("resources/tool-cursor.png");
		pencilIcon = new ImageIcon("resources/tool-pencil.png");
		eraserIcon = new ImageIcon("resources/tool-eraser.png");
		
	    // create 'cursor' button
		cursorButton = new VToggleButton(cursorIcon, "tt_Pointer");
		cursorButton.setActionCommand("cursor");
		cursorButton.addActionListener(controller);
		cursorButton.setSelected(true); // cursor selected per default
		toolButtons.add(cursorButton);
		toolToolBar.add(cursorButton);
		
		// create 'pencil' button
		pencilButton = new VToggleButton(pencilIcon, "tt_Pencil");
		pencilButton.setActionCommand("pencil");
		pencilButton.addActionListener(controller);
		toolButtons.add(pencilButton);
		toolToolBar.add(pencilButton);
		
		// create 'eraser' button
		eraserButton = new VToggleButton(eraserIcon, "tt_Eraser");
		eraserButton.setActionCommand("eraser");
		eraserButton.addActionListener(controller);
		toolButtons.add(eraserButton);
		toolToolBar.add(eraserButton);
		
		toolToolBar.addSeparator();
		
		/* add toolbar to panel */
		this.add(toolToolBar);
		
		/* Window toolbar */
		JToolBar windowToolBar = new JToolBar();
		windowToolBar.setLayout(toolBarLayout);
	    windowToolBar.setFloatable(false);
		
		// Initialize toolbar icons
		keyboardIcon = new ImageIcon("resources/window-keyboard.png");
		
		// Create the 'keyboard' button
	    keyboardButton = new VButton(keyboardIcon, "tt_Keyboard");
	    keyboardButton.setActionCommand("keyboard");
	    keyboardButton.addActionListener(controller);
	    windowToolBar.add(keyboardButton);
	    
	    windowToolBar.addSeparator();
	    
	    this.add(windowToolBar);
		
		// create 'Timesignature' ComboBox
	    
	    String[] lista = {"1/1", "1/2", "1/4", "1/8", "1/16", "1/32", "1/64"};
	    
	    timeSignatureBox = new VComboBox(lista, "tt_Resolution");
	    timeSignatureBox.setActionCommand("timeSignatureBox");
	    timeSignatureBox.addItemListener(this);				
		windowToolBar.add(timeSignatureBox);
	    
		/* Zoom toolbar */
		JToolBar zoomToolBar = new JToolBar();
		zoomToolBar.setLayout(toolBarLayout);
		zoomToolBar.setFloatable(false);
		
		ImageIcon zoomIcon = new ImageIcon("resources/zoom.png");
		zoomLabel = new JLabel();
		zoomLabel.setIcon(zoomIcon);
		zoomToolBar.add(zoomLabel);
		
		// create 'zoom' Slider
		zoom = new VSlider(SwingConstants.HORIZONTAL, MINZOOM, MAXZOOM, INITZOOM, "tt_Zoomslider");
		//zoom.setSize(5, 5);
		zoom.addChangeListener(this);
		zoom.setMajorTickSpacing(TICK_SPACE);
		zoom.setPaintTicks(true);
		zoom.setSnapToTicks(true);
		zoom.setPaintTicks(false);
		
		//Create the label table
		Hashtable<Integer,JLabel> labelTable = new Hashtable<Integer,JLabel>();
		labelTable.put( new Integer( 0 ), new JLabel("MIN") );
		labelTable.put( new Integer( 10 ), new JLabel("MAX") );
		zoom.setLabelTable( labelTable );

		zoomToolBar.add(zoom);
		
		/* add toolbar to panel */
		this.add(zoomToolBar);
		
		UIManager.put("Slider.paintValue", true);	// enables the showing of slider value in GTK
		
		zoomToolBar.addSeparator();
				
		updateEnabledState();
	}

	// Handles the enabled state of the toolbar's buttons.
	private void updateEnabledState(){
	
		// Some booleans used to control
		boolean projectsOpen = App.hasProjects();
		boolean selectedTracks = projectsOpen && !App.UI.getTrackSelection().isEmpty(); // Thanks to lazy evaluation we can safetly write like this :)
		boolean isPlaying = projectsOpen && App.Project.isPlaying();
	
		saveButton.setEnabled(projectsOpen && !isPlaying && !App.Project.getIsSaved());
		saveAsButton.setEnabled(projectsOpen && !isPlaying);
		createTrackButton.setEnabled(projectsOpen && !isPlaying);
		recordButton.setEnabled(projectsOpen && App.UI.getTrackSelection().size() == 1);
		recordButton.setSelected(projectsOpen && App.Project.isRecordEnabled());
		
		undoButton.setEnabled(projectsOpen && !isPlaying && App.Project.canUndo());
		redoButton.setEnabled(projectsOpen && !isPlaying && App.Project.canRedo());
		
		newButton.setEnabled(!isPlaying);
		openButton.setEnabled(!isPlaying);
		
		playPauseButton.setEnabled(projectsOpen);
		stopButton.setEnabled(projectsOpen);
		keyboardButton.setEnabled(projectsOpen);
		timeSignatureBox.setEnabled(projectsOpen);
		
		overviewButton.setEnabled(selectedTracks);
		pianoRollButton.setEnabled(selectedTracks);
		notationButton.setEnabled(selectedTracks);
		
		removeTrackButton.setEnabled(!isPlaying && selectedTracks && App.Project.getTracks().length > 0);
		cursorButton.setEnabled(!isPlaying && selectedTracks);
		pencilButton.setEnabled(!isPlaying && selectedTracks && App.UI.getPerspective() == Perspective.PIANOROLL );
		eraserButton.setEnabled(!isPlaying && selectedTracks && App.UI.getPerspective() == Perspective.PIANOROLL );
		
		zoom.setEnabled(!isPlaying && projectsOpen);
		zoomLabel.setEnabled(!isPlaying && projectsOpen);
		
	}

	@Override
	public void update(Observable o, Object arg) {

		// Check which type of action that was performed
		Action action = (Action) arg;  

		// Update the enabled state for the tool bar controls 
		updateEnabledState();
		
		// Then perform the desired updates depending on the action
    	switch( action ){
    	
		case SEQUENCER_STARTED:
			playPauseButton.setIcon(pauseIcon);
    		playPauseButton.setActionCommand("pause");
    		playPauseButton.setToolTipKey("tt_Pause");
    		break;
		
		case PROJECT_CLOSED:
		case SEQUENCER_STOPPED:
			playPauseButton.setIcon(playIcon);
    		playPauseButton.setActionCommand("play"); 
    		playPauseButton.setToolTipKey("tt_Play");
    		break;
    	
		case PERSPECTIVE_CHANGED:
			
			switch( App.UI.getPerspective() ){
			case OVERVIEW:
				overviewButton.setSelected(true);
				break;
			case PIANOROLL:
				pianoRollButton.setSelected(true);
				break;
			case NOTATION:
				notationButton.setSelected(true);
				break;
			}
			break;
    	}
    	
	}

	/** Updates the GUI model when the zoom slider value is changed. */
    public void stateChanged(ChangeEvent e) {
    	App.UI.setZoomLevel(((JSlider)e.getSource()).getValue());
    }
    
    /** Updates the UI model when the grid resolution is changed. */
    public void itemStateChanged(ItemEvent e) {
    	
    	int gridResolution;
    	if( e.getItem().equals("1/1")) {
			gridResolution = 1;
		} else if ( e.getItem().equals("1/2") ) {
			gridResolution = 2;
		} else if ( e.getItem().equals("1/4") ) {
			gridResolution = 4;
		} else if ( e.getItem().equals("1/8") ) {
			gridResolution = 8;
		} else if ( e.getItem().equals("1/16") ) {
			gridResolution = 16;
		} else if ( e.getItem().equals("1/32") ) {
			gridResolution = 32;
		} else {
			gridResolution = 64;
		}

    	// Update the model
    	App.UI.setResolution(gridResolution);

    }
	
    
}
